Skip to content

Commit

Permalink
feat: 🧪 Support folder namespaces for intl (#389)
Browse files Browse the repository at this point in the history
Fixes #344

Ember Intl exposes a configuration file which allows you to configure the localization setup. One such setting is `wrapTranslationsWithNamespace` which determines whether translation keys should have the folder names within the key names.

#### `wrapTranslationsWithNamespace: false` (default)
```yml
# translations/sub-folder/en-us.yml
subFolder:
  foo: bar
```
```js
this.intl.t('subFolder.foo');
```

#### `wrapTranslationsWithNamespace: true`
```yml
# translations/sub-folder/en-us.yml
subFolder:
  foo: bar
```
```js
this.intl.t('sub-folder.subFolder.foo');
```
`wrapTranslationsWithNamespace: true` is not currently supported by this extension. This PR adds the ability, when `wrapTranslationsWithNamespace: true` is set in `app/config/ember-intl.js`, to support keys which have folders in their key names.

Co-authored-by: Pat O'Callaghan <pat@intercom.io>
  • Loading branch information
achambers and patocallaghan authored Jul 22, 2022
1 parent 83651b4 commit 9099c60
Show file tree
Hide file tree
Showing 2 changed files with 548 additions and 133 deletions.
62 changes: 54 additions & 8 deletions src/builtin-addons/core/intl-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { Location, Range } from 'vscode-languageserver';
import { URI } from 'vscode-uri';
import { Server } from '../..';
import { logDebugInfo } from '../../utils/logger';
import { getRequireSupport } from '../../utils/layout-helpers';

type Translations = {
locale: string;
Expand All @@ -14,6 +15,10 @@ type Translations = {
};
type TranslationsHashMap = Record<string, Translations[]>;

type EmberIntlConfig = {
wrapTranslationsWithNamespace?: boolean;
};

type TranslationFile =
| {
type: 'json';
Expand All @@ -30,20 +35,43 @@ type TranslationFile =

const YAML_EXTENSIONS = ['.yaml', '.yml'];

export function getEmberIntlConfig(root: string): EmberIntlConfig | null {
try {
if (!getRequireSupport()) {
return null;
}

// @ts-expect-error @todo - fix webpack imports
const requireFunc = typeof __webpack_require__ === 'function' ? __non_webpack_require__ : require;

// eslint-disable-next-line @typescript-eslint/no-var-requires
const intlConfig = requireFunc(path.join(root, 'config', 'ember-intl.js'));

if (!intlConfig) {
return null;
}

return typeof intlConfig === 'function' ? intlConfig() : intlConfig;
} catch (e) {
// logDebugInfo('catch', e);
return null;
}
}

export async function getTranslations(root: string, server: Server): Promise<TranslationsHashMap> {
const hashMap = {};
const intlEntry = path.join(root, 'translations');

const intlEntryExists = await server.fs.exists(intlEntry);

if (intlEntryExists) {
await recursiveIntlTranslationsSearch(server, hashMap, intlEntry);
await recursiveIntlTranslationsSearch(server, hashMap, intlEntry, root);
}

return hashMap;
}

async function recursiveIntlTranslationsSearch(server: Server, hashMap: TranslationsHashMap, startPath: string) {
async function recursiveIntlTranslationsSearch(server: Server, hashMap: TranslationsHashMap, startPath: string, root: string) {
const localizations = await server.fs.readDirectory(startPath);

for (const [fileName] of localizations) {
Expand All @@ -55,15 +83,15 @@ async function recursiveIntlTranslationsSearch(server: Server, hashMap: Translat
const fileStats = await server.fs.stat(filePath);

if (fileStats.isDirectory()) {
await recursiveIntlTranslationsSearch(server, hashMap, filePath);
await recursiveIntlTranslationsSearch(server, hashMap, filePath, root);
} else {
const translationFile = await objFromFile(server, filePath);

if (!translationFile.json) {
return;
}

addToHashMap(hashMap, translationFile, localization, filePath);
addToHashMap(hashMap, translationFile, localization, filePath, root);
}
} catch (e) {
logDebugInfo('error', e);
Expand Down Expand Up @@ -100,13 +128,31 @@ async function objFromFile(server: Server, filePath: string): Promise<Translatio
return {};
}

function addToHashMap(hash: TranslationsHashMap, translationFile: TranslationFile, locale: string, filePath: string) {
function generateTranslationKey(key: string, filePath: string, root: string): string {
const config = getEmberIntlConfig(root);
const wrapTranslationsWithNamespace = config?.wrapTranslationsWithNamespace;

if (!wrapTranslationsWithNamespace) {
return key;
}

const dirname = path
.dirname(filePath)
.replace(new RegExp(`.*\\${path.sep}translations\\${path.sep}`), '')
.replace(new RegExp(`\\${path.sep}`, 'g'), '.');

return `${dirname}.${key}`;
}

function addToHashMap(hash: TranslationsHashMap, translationFile: TranslationFile, locale: string, filePath: string, root: string) {
const items: Record<string, string> = flat(translationFile.json);
const extension = path.extname(filePath);

Object.keys(items).forEach((p) => {
if (!(p in hash)) {
hash[p] = [];
const key = generateTranslationKey(p, filePath, root);

if (!(key in hash)) {
hash[key] = [];
}

const uri = URI.file(filePath).toString();
Expand All @@ -124,7 +170,7 @@ function addToHashMap(hash: TranslationsHashMap, translationFile: TranslationFil
const endColumn = position ? position.end.column - 1 : 0;
const range = Range.create(startLine, startColumn, endLine, endColumn);

hash[p].push({ locale, text: items[p], location: Location.create(uri, range) });
hash[key].push({ locale, text: items[p], location: Location.create(uri, range) });
});
}

Expand Down
Loading

0 comments on commit 9099c60

Please # to comment.