Skip to content

Commit

Permalink
feat: use fallback locales from cldr (#820)
Browse files Browse the repository at this point in the history
  • Loading branch information
semoal authored Nov 5, 2020
1 parent 22667ad commit 2d9e124
Show file tree
Hide file tree
Showing 11 changed files with 397 additions and 81 deletions.
50 changes: 41 additions & 9 deletions docs/ref/conf.rst
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ Default config:
}],
"compileNamespace": "cjs",
"extractBabelOptions": {},
"fallbackLocale": "",
"fallbackLocales": {},
"format": "po",
"locales": [],
"orderBy": "messageId",
Expand Down Expand Up @@ -230,17 +230,49 @@ extracted. This is required when project doesn't use standard Babel config
}
}
.. config:: fallbackLocale
.. config:: fallbackLocales

fallbackLocale
fallbackLocales
--------------

Default: ``''``
Default: ``{}``

:conf:`fallbackLocales` by default is using `CLDR Parent Locales <https://github.com/unicode-cldr/cldr-core/blob/master/supplemental/parentLocales.json>`_, unless you disable it with a `false`:

.. code-block:: json
{
"fallbackLocales": false
}
:conf:`fallbackLocales` object let's us configure fallback locales to each locale instance.

.. code-block:: json
{
"fallbackLocales": {
"en-US": ["en-GB", "en"],
"es-MX": "es"
}
}
On this example if any translation isn't found on `en-US` then will search on `en-GB`, after that if not found we'll search in `en`

Also, we can configure a default one for everything:

.. code-block:: json
{
"fallbackLocales": {
"en-US": ["en-GB", "en"],
"es-MX": "es",
"default": "en"
}
}
Translation from :conf:`fallbackLocale` is used when translation for given locale is missing.
Translations from :conf:`fallbackLocales` is used when translation for given locale is missing.

If :conf:`fallbackLocale` isn't defined or translation in :conf:`fallbackLocale` is
missing too, either default message or message ID is used instead.
If :conf:`fallbackLocales` is `false` default message or message ID is used instead.

.. config:: format

Expand Down Expand Up @@ -393,6 +425,6 @@ Catalog for :conf:`sourceLocale` doesn't require translated messages, because me
IDs are used by default. However, it's still possible to override message ID by
providing custom translation.

The difference between :conf:`fallbackLocale` and :conf:`sourceLocale` is that
:conf:`fallbackLocale` is used in translation, while :conf:`sourceLocale` is
The difference between :conf:`fallbackLocales` and :conf:`sourceLocale` is that
:conf:`fallbackLocales` is used in translation, while :conf:`sourceLocale` is
used for the message ID.
35 changes: 27 additions & 8 deletions packages/cli/src/api/catalog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import glob from "glob"
import micromatch from "micromatch"
import normalize from "normalize-path"

import { LinguiConfig, OrderBy } from "@lingui/conf"
import { LinguiConfig, OrderBy, FallbackLocales } from "@lingui/conf"

import getFormat from "./formats"
import { CatalogFormatter } from "./formats/types"
Expand Down Expand Up @@ -46,7 +46,7 @@ export type MergeOptions = {

export type GetTranslationsOptions = {
sourceLocale: string
fallbackLocale: string
fallbackLocales: FallbackLocales
}

type CatalogProps = {
Expand Down Expand Up @@ -94,7 +94,7 @@ export class Catalog {
)
) as unknown) as (catalog: AllCatalogsType) => AllCatalogsType

const sortedCatalogs = cleanAndSort(catalogs);
const sortedCatalogs = cleanAndSort(catalogs)

if (options.locale) {
this.write(options.locale, sortedCatalogs[options.locale])
Expand Down Expand Up @@ -227,26 +227,45 @@ export class Catalog {
catalogs: Object,
locale: string,
key: string,
{ fallbackLocale, sourceLocale }: GetTranslationsOptions
{ fallbackLocales, sourceLocale }: GetTranslationsOptions
) {
if (!catalogs[locale].hasOwnProperty(key)) {
console.error(`Message with key ${key} is missing in locale ${locale}`)
}

const getTranslation = (locale) => catalogs[locale][key].translation

const getMultipleFallbacks = (locale) => {
const fL = fallbackLocales[locale]

// some probably the fallback will be undefined, so just search by locale
if (!fL) return null

if (Array.isArray(fL)) {
for (const fallbackLocale of fL) {
if (catalogs[fallbackLocale]) {
return getTranslation(fallbackLocale)
}
}
} else {
return getTranslation(fL)
}
}

return (
// Get translation in target locale
getTranslation(locale) ||
// Get translation in fallbackLocale (if any)
(fallbackLocale && getTranslation(fallbackLocale)) ||
// We search in fallbackLocales as dependent of each locale
getMultipleFallbacks(locale) ||
// Get translation in fallbackLocales.default (if any)
(fallbackLocales.default && getTranslation(fallbackLocales.default)) ||
// Get message default
catalogs[locale][key].defaults ||
// If sourceLocale is either target locale of fallback one, use key
(sourceLocale && sourceLocale === locale && key) ||
(sourceLocale &&
fallbackLocale &&
sourceLocale === fallbackLocale &&
fallbackLocales.default &&
sourceLocale === fallbackLocales.default &&
key) ||
// Otherwise no translation is available
undefined
Expand Down
10 changes: 9 additions & 1 deletion packages/cli/src/api/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,16 @@ export type AllCatalogsType = {
[locale: string]: CatalogType
}

export type LocaleObject = {
[locale: string]: string[] | string
}
export type DefaultLocaleObject = {
default: string
}
export declare type FallbackLocales = LocaleObject | DefaultLocaleObject | false

export type getTranslationOptions = {
fallbackLocale: string
fallbackLocales: FallbackLocales
sourceLocale: string
}

Expand Down
2 changes: 1 addition & 1 deletion packages/cli/src/lingui-compile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ function command(config, options) {
const messages = catalog.getTranslations(
locale === config.pseudoLocale ? config.sourceLocale : locale,
{
fallbackLocale: config.fallbackLocale,
fallbackLocales: config.fallbackLocales,
sourceLocale: config.sourceLocale,
}
)
Expand Down
24 changes: 18 additions & 6 deletions packages/conf/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,22 @@ declare type CatalogConfig = {
include: string[];
exclude?: string[];
};

export type LocaleObject = {
[locale: string]: string[] | string
}

export type DefaultLocaleObject = {
default: string
}

export declare type FallbackLocales = LocaleObject | DefaultLocaleObject

export declare type LinguiConfig = {
catalogs: CatalogConfig[];
compileNamespace: string;
extractBabelOptions: Object;
fallbackLocale: string;
fallbackLocales: FallbackLocales;
format: CatalogFormat;
prevFormat: CatalogFormat;
formatOptions: CatalogFormatOptions;
Expand Down Expand Up @@ -42,7 +53,7 @@ export declare const configValidation: {
};
catalogs: CatalogConfig[];
compileNamespace: string;
fallbackLocale: string;
fallbackLocales: FallbackLocales;
format: CatalogFormat;
formatOptions: CatalogFormatOptions;
locales: string[];
Expand All @@ -53,7 +64,7 @@ export declare const configValidation: {
sourceLocale: string;
};
deprecatedConfig: {
fallbackLanguage: (config: LinguiConfig & DeprecatedFallbackLanguage) => string;
fallbackLocale: (config: LinguiConfig & DeprecatedFallbackLanguage) => string;
localeDir: (config: LinguiConfig & DeprecatedLocaleDir) => string;
srcPathDirs: (config: LinguiConfig & DeprecatedLocaleDir) => string;
srcPathIgnorePatterns: (config: LinguiConfig & DeprecatedLocaleDir) => string;
Expand All @@ -62,14 +73,15 @@ export declare const configValidation: {
};
export declare function replaceRootDir(config: LinguiConfig, rootDir: string): LinguiConfig;
/**
* Replace fallbackLanguage with fallbackLocale
* Replace fallbackLocale with fallbackLocales
*
* Released in lingui-conf 0.9
* Remove anytime after 3.x
* Remove anytime after 4.x
*/
declare type DeprecatedFallbackLanguage = {
fallbackLanguage: string | null;
fallbackLocale: string | null;
};

export declare function fallbackLanguageMigration(config: LinguiConfig & DeprecatedFallbackLanguage): LinguiConfig;
/**
* Replace localeDir, srcPathDirs and srcPathIgnorePatterns with catalogs
Expand Down
24 changes: 23 additions & 1 deletion packages/conf/src/__snapshots__/index.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,26 @@ Documentation: https://lingui.js.org/ref/conf.html
Please update your configuration.
Documentation: https://lingui.js.org/ref/conf.html
`;

exports[`@lingui/conf fallbackLocales logic if fallbackLocale is defined, we use the default one on fallbackLocales 1`] = `
● Deprecation Warning:
Option fallbackLocale was replaced by fallbackLocales
You can find more information here: https://github.com/lingui/js-lingui/issues/791
@lingui/cli now treats your current configuration as:
{
"fallbackLocales": {
default: "en"
}
}
Please update your configuration.
Documentation: https://lingui.js.org/ref/conf.html
`;

Expand All @@ -93,7 +113,9 @@ Object {
plugins: Array [],
presets: Array [],
},
fallbackLocale: ,
fallbackLocales: Object {
en-gb: en,
},
format: po,
formatOptions: Object {
origins: true,
Expand Down
7 changes: 7 additions & 0 deletions packages/conf/src/fixtures/valid/.fallbacklocalesrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
locales: ["en-US", "es-MX"],
fallbackLocales: {
"en-US": ["en"],
"default": "en"
}
}
37 changes: 37 additions & 0 deletions packages/conf/src/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import path from "path"
import mockFs from "mock-fs"
import { validate } from "jest-validate"
import {
getConfig,
Expand Down Expand Up @@ -142,4 +143,40 @@ describe("@lingui/conf", function () {
})
})
})

describe("fallbackLocales logic", () => {
afterEach(() => {
mockFs.restore()
})

it ("if fallbackLocale is defined, we use the default one on fallbackLocales", () => {
mockFs({
".linguirc": JSON.stringify({
locales: ["en-US"],
fallbackLocale: "en"
})
})
mockConsole((console) => {
const config = getConfig({
configPath: ".linguirc",
})
expect(config.fallbackLocales.default).toEqual("en")
expect(getConsoleMockCalls(console.warn)).toMatchSnapshot()
})
})

it ("if fallbackLocales is defined, we also build the cldr", () => {
const config = getConfig({
configPath: path.resolve(
__dirname,
path.join("fixtures", "valid", ".fallbacklocalesrc")
),
})
expect(config.fallbackLocales).toEqual({
"en-US": "en",
default: "en",
"es-MX": "es"
})
})
})
})
Loading

1 comment on commit 2d9e124

@vercel
Copy link

@vercel vercel bot commented on 2d9e124 Nov 5, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.